/*
	Copyright (C) 2009 Salil Bhagurkar
	
	This file is part of ViSE
	
	ViSE is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	ViSE is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.
	
	You should have received a copy of the GNU Lesser General Public License
	along with ViSE.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <shared.h>

static int b8000_fs_write(struct node_fs *this, void *buf, u32 offset, u32 count);
int init_b8000(struct node_fs *this);

static struct node_fs b8000_console = {
	.type = TYPE_FS_CLIENT,
	.name = "b8000",
	.init = init_b8000,
	.read = dummy,
	.write = b8000_fs_write,
	.enumerate = dummy,
	.notify = dummy,
	.class = dummy,
	NODE_FS_INIT_VARS,
	NODE_FS_LIST_INIT,
};


static u8 *vgafb = (u8 *)0xb8000;
static u16 cursor = 0;

static void set_hw_cursor()
{
	outb(0x3d4, 15);
	outb(0x3d5,(u8)cursor);
	outb(0x3d4, 14);
	outb(0x3d5,(u8)(cursor>>8));
}

static u16 get_hw_cursor()
{
	u16 cur;
	outb(0x3d4, 15);
	cur|=inb(0x3d5);
 	outb(0x3d4, 14);
 	cur|=(inb(0x3d5)<<8);
	return cur;
}


static void scroll_screen(void)
{
	int i;
	unsigned int *vidmem=(unsigned int *)vgafb;
	for(i=0;i<80*24;i+=8)	{
		*(vidmem+i)=*(vidmem+i+40);
		*(vidmem+i+1)=*(vidmem+i+1+40);
		*(vidmem+i+2)=*(vidmem+i+2+40);
		*(vidmem+i+3)=*(vidmem+i+3+40);
		*(vidmem+i+4)=*(vidmem+i+4+40);
		*(vidmem+i+5)=*(vidmem+i+5+40);
		*(vidmem+i+6)=*(vidmem+i+6+40);
		*(vidmem+i+7)=*(vidmem+i+7+40);
	}
	cursor -= 80;
}

//Function called from architecture as the first console output
void b8000_write(const char *buf)
{
	unsigned char *vidmem;
	while(*buf) {
		//arch_delay_ms(2);
		if((*buf) == '\n') {
			cursor = cursor/80 * 80 + 80;
		} else if((*buf) == '\b') {
			vidmem = vgafb + cursor * 2;
			*(vidmem-2) = 0;
			cursor--;
		} else {
			vidmem = vgafb + cursor * 2;
			*vidmem = *buf;
			*(vidmem+1) = 7;
			cursor++;
		}
		buf++;
		if(cursor >= 2000) {
			scroll_screen();
		}
	}
	set_hw_cursor();
}

void clrscr()
{
	u32 i;
	u16 *p;
	p = (u16 *)vgafb;
	for(i=0; i<2000; i++) {
		*p++ = 0x700;
	}
}
\

static int b8000_fs_write(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	b8000_write(buf);
	return 0;
}

int init_b8000(struct node_fs *this)
{
	clrscr();
	cursor = 0;
	set_hw_cursor();
	return 0;
}


int text_read(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	return 0;
}

int text_write(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	switch(offset) {
		case DRIVER_PROBE:
			return 0;
		case DRIVER_START:
			fs_link("/class/console", &b8000_console);
			//We got initiated by the debug message output code in arch/debug.c
			//b8000_console.init(&b8000_console);
			return 0;
		default:
			return ENOSUP;
	}
	return 0;
}


struct node_fs text_driver = {
	.type = TYPE_FS_BOTH,
	.name = "text_driver",
	NODE_FS_INIT_VARS,
	.read = text_read,
	.write = text_write,
	.enumerate = dummy,
	.notify = dummy,
	NODE_FS_INIT_INIT,
	NODE_FS_LIST_INIT,
};


int register_b8000()
{
	fs_link("/driver", &text_driver);
	return 0;
}
